/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "literal_data_extractor.h"
#include "js_function_kind.h"
#include "plugins/ecmascript/runtime/base/string_helper.h"
#include "plugins/ecmascript/runtime/ecma_string.h"

#include "plugins/ecmascript/runtime/js_thread.h"
#include "plugins/ecmascript/runtime/tagged_array-inl.h"
#include "libpandafile/literal_data_accessor-inl.h"

namespace ark::ecmascript {
using LiteralTag = panda_file::LiteralTag;
using StringData = panda_file::StringData;
using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue;

void LiteralDataExtractor::ExtractObjectDatas(JSThread *thread, const panda_file::File *pf, size_t index,
                                              JSMutableHandle<TaggedArray> elements,
                                              JSMutableHandle<TaggedArray> properties, PandaFileTranslator *pft)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    LOG_ECMA(DEBUG) << "Panda File" << pf->GetFilename();
    panda_file::File::EntityId literalArraysId = pf->GetLiteralArraysId();
    panda_file::LiteralDataAccessor lda(*pf, literalArraysId);

    uint32_t num = lda.GetLiteralValsNum(index) / 2;  // 2: half
    elements.Update(factory->NewTaggedArray(num, JSTaggedValue::Hole(), ark::SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT)
                        .GetTaggedValue());
    properties.Update(factory->NewTaggedArray(num, JSTaggedValue::Hole(), ark::SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT)
                          .GetTaggedValue());
    uint32_t epos = 0;
    uint32_t ppos = 0;
    const uint8_t pairSize = 2;
    lda.EnumerateLiteralVals(index, [elements, properties, &epos, &ppos, factory, thread, pft,
                                     pf](const LiteralValue &value, const LiteralTag &tag) {
        JSTaggedValue jt = JSTaggedValue::Null();
        bool flag = false;
        switch (tag) {
            case LiteralTag::INTEGER: {
                jt = JSTaggedValue(std::get<uint32_t>(value));
                break;
            }
            case LiteralTag::DOUBLE: {
                jt = JSTaggedValue(std::get<double>(value));
                break;
            }
            case LiteralTag::BOOL: {
                jt = JSTaggedValue(std::get<bool>(value));
                break;
            }
            case LiteralTag::STRING: {
                StringData sd = pf->GetStringData(panda_file::File::EntityId(std::get<uint32_t>(value)));
                EcmaString *str = factory->GetRawStringFromStringTable(sd.data, sd.utf16Length, sd.isAscii);
                jt = JSTaggedValue(str);
                uint32_t idx = 0;
                if (JSTaggedValue::ToElementIndex(jt, &idx) && ppos % pairSize == 0) {
                    flag = true;
                }
                break;
            }
            case LiteralTag::METHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::NORMAL_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::GENERATORMETHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::GENERATOR_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::ASYNCGENERATORMETHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc =
                    pft->DefineMethodInLiteral(methodId, FunctionKind::ASYNC_GENERATOR_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::ASYNCMETHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::ASYNC_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::ACCESSOR: {
                JSHandle<AccessorData> accessor =
                    factory->NewAccessorData(ark::SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT);
                jt = JSTaggedValue(accessor.GetTaggedValue());
                break;
            }
            case LiteralTag::NULLVALUE: {
                break;
            }
            default: {
                UNREACHABLE();
                break;
            }
        }
        if (epos % pairSize == 0 && !flag) {
            properties->Set(thread, ppos++, jt);
        } else {
            elements->Set(thread, epos++, jt);
        }
    });
}

JSHandle<TaggedArray> LiteralDataExtractor::GetDatasIgnoreType(JSThread *thread, const panda_file::File *pf,
                                                               size_t index, PandaFileTranslator *pft)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    LOG_ECMA(DEBUG) << "Panda File" << pf->GetFilename();
    panda_file::File::EntityId literalArraysId = pf->GetLiteralArraysId();
    panda_file::LiteralDataAccessor lda(*pf, literalArraysId);

    uint32_t num = lda.GetLiteralValsNum(index) / 2;  // 2: half
    JSHandle<TaggedArray> literals =
        factory->NewTaggedArray(num, JSTaggedValue::Hole(), ark::SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT);
    uint32_t pos = 0;
    lda.EnumerateLiteralVals(index, [literals, &pos, factory, thread, pft,
                                     pf](const panda_file::LiteralDataAccessor::LiteralValue &value,
                                         const LiteralTag &tag) {
        JSTaggedValue jt = JSTaggedValue::Null();
        switch (tag) {
            case LiteralTag::INTEGER: {
                jt = JSTaggedValue(std::get<uint32_t>(value));
                break;
            }
            case LiteralTag::DOUBLE: {
                jt = JSTaggedValue(std::get<double>(value));
                break;
            }
            case LiteralTag::BOOL: {
                jt = JSTaggedValue(std::get<bool>(value));
                break;
            }
            case LiteralTag::STRING: {
                StringData sd = pf->GetStringData(panda_file::File::EntityId(std::get<uint32_t>(value)));
                EcmaString *str = factory->GetRawStringFromStringTable(sd.data, sd.utf16Length, sd.isAscii);
                jt = JSTaggedValue(str);
                break;
            }
            case LiteralTag::METHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::NORMAL_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::GENERATORMETHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::GENERATOR_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::ASYNCGENERATORMETHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc =
                    pft->DefineMethodInLiteral(methodId, FunctionKind::ASYNC_GENERATOR_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::ASYNCMETHOD: {
                ASSERT(pft != nullptr);
                uint32_t methodId = std::get<uint32_t>(value);
                JSHandle<JSFunction> jsFunc = pft->DefineMethodInLiteral(methodId, FunctionKind::ASYNC_FUNCTION);
                jt = jsFunc.GetTaggedValue();
                break;
            }
            case LiteralTag::ACCESSOR: {
                JSHandle<AccessorData> accessor =
                    factory->NewAccessorData(ark::SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT);
                jt = accessor.GetTaggedValue();
                break;
            }
            case LiteralTag::NULLVALUE: {
                break;
            }
            default: {
                UNREACHABLE();
                break;
            }
        }
        literals->Set(thread, pos++, jt);
    });
    return literals;
}
}  // namespace ark::ecmascript
